import argparse
import base64
import hashlib
import io
import json
import os
import sys
import urllib.parse
try:
    from flask import Flask, Request, request, send_file, abort
    from flask_caching import Cache
    from PIL import Image
    import pinyin
except:
    print('依赖缺失，需要首先安装依赖. python3 -m pip install --upgrade pip; python3 -m pip install flask flask-caching Pillow pinyin')
    sys.exit()
    # python3 -m pip install --upgrade pip

'''
一个单文件的本地图片查看服务，。
依赖 Flask、Flask-Caching、Pillow、pinyin。
python3 -m pip install --upgrade pip; python3 -m pip install flask flask-caching Pillow pinyin
运行：
python3 flask_img_viewer.py --root=图片资源目录，不指定则当前目录
'''

app          = Flask(__name__)
cache        = Cache(app, with_jinja2_ext=False, config={'CACHE_TYPE': 'SimpleCache'})
req: Request = request


APP_PORT     = 8011
HOME_TEMPLATE = '''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Wallpapers</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="https://res-cdn.n.gongyibao.cn/res_cdn/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://res-cdn.n.gongyibao.cn/res_cdn/artDialog/7.0.0/dialog-plus.js?0.1"></script>
<link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/c/font_1937037_m5dhx3mr8mp.css" />
<style>
html, body { padding:0; margin:0; background:#000; }
div { box-sizing:border-box; }
.ui-dialog { border:none !important; background: #333 !important; }
.ui-dialog-content { overflow:hidden !important; }
ol, ul { margin:0; padding:0; list-style:none;display:block;box-sizing: border-box; margin-top:50px; }
li { margin:2px; padding:0; width:200px; height:113px; overflow: hidden;display:block; float:left;box-sizing: border-box; border: solid 1px #333; }
.search_box   { font-size:1.1em; text-align:center; position: fixed; top: 0px; display: block; width: 100%; background: #222; padding-top:5px; height:45px; color:#fff; }
.search_box * { font-size:1.1em; }
.search_box input[type=text] { width: 150px;border: solid 1px #333;text-align: center; }
.search_box input[type=checkbox] { width:20px;height:20px; }
.search_box input[type=button] { background: #090;color: #fff;border: 1px solid #090; }
.search_box input { box-sizing: border-box; vertical-align: middle; height: 30px; border-radius: 5px; }
.d-box { position: fixed; top:0; z-index: 99999999; text-align: center;display: block; width:100%; font-size:16px; }
.d-box-child { background: #ffc; padding: 0 5px; display: inline-block; border-radius: 0 0 10px 10px; height:25px; overflow: hidden; }
.d-box-child * { vertical-align: middle; }
.dialog_title { overflow:hidden; height:16px; }
.dialog_close { padding: 0;color:red;cursor: pointer;border: solid 1px #ffe300;line-height:16px;height:16px;width:16px;display:inline-block; }
.loading-box { background-repeat: no-repeat;background-position: 50% 50%;margin-right: 5px; background-image: url(); }
.loading-box:before { content: "."; font-size:1px; line-height:16px; width:16px; height:16px; display: inline-block; }
</style>
<script>
var d;
var imgIndex = -1;
function showImg(f, idx) {
    imgIndex = idx;
    d = dialog({
        padding:0,width:$(window).width(),height:$(window).height(),quickClose: true, 
        content:`<div class="d-box">
                    <div class="d-box-child">
                        <span class="dialog_title">${decodeURIComponent(f)}</span>
                        <span onclick="d.close().remove();" class="dialog_close" title="关闭 Close">x</span>
                    </div>
                </div> 
                <img src="/viewer?name=${encodeURIComponent(f)}" width="100%">`
    }).showModal();
    return false;
}
function search_keypress(event, sender) {
    if(event.which === 13) {
        $('#s_sub').click();
    }
}
function search_sub(path, sender) {
    let href = '?name=' + encodeURIComponent($('#s_name').val());
    if($('#s_safe').prop('checked')) href += '&safe=1'
    if(path !== '') href += '&path=' + encodeURIComponent(path)
    location.href = href;
}
var imgs_arr = {imgs_arr};
</script>
<link rel="shortcut icon" href="" />
</head>
<body>
<div class="search_box">文件名：
<input type="text" name="s_name" id="s_name" value="{search_value}" onkeypress="search_keypress(event, this)" />
<input type="checkbox" id="s_safe" {safe_checked} />
<input type="button" id="s_sub" value="搜索" onclick="search_sub('{search_path}', this)" />
</div>
<ol id="imgs_box">
{imgs_list}
</ol>
<script>
var playerTimer;
function player(xflag, key) {
    let img = $('.ui-dialog-content img');
    if(img.length != 1) {
        clearInterval(playerTimer);
        playerTimer=0;
        return;
    }
    if(key === '+') {
        imgIndex += 1;
        if(imgIndex >= imgs_arr.length) imgIndex = 0;
    } else if(key === '-') {
        imgIndex -= 1;
        if(imgIndex < 0) imgIndex = imgs_arr.length - 1;
    }
    if(d.width() != $(window).width()) d.width($(window).width());
    if(d.height() != $(window).height()) d.height($(window).height());
    // slideUp() slideDown()
    // fadeOut() fadeIn()
    img.fadeOut(500, function() {
        let that = $(this);
        that.attr('src', './viewer?name=' + encodeURIComponent(imgs_arr[imgIndex]) + '');
        let d_title = $('.ui-dialog-content .dialog_title').text(imgs_arr[imgIndex]);
        if(playerTimer) {
            d_title.prepend('<i class="iconfont loading-box" />');
        }
        setTimeout(()=>{ that.fadeIn(1000); }, 200);
    });
}
$(function() {
    let imgs_box = $('#imgs_box');
    for(let i=0; i < imgs_arr.length; i++) {
        let f = imgs_arr[i];
        imgs_box.append('<li><a href="./viewer?name=' + encodeURIComponent(f) + '" onclick="return showImg(\\'' + f + '\\', ' + i + ')"><img src="./viewer?resize=1&name=' + encodeURIComponent(f) + '" width="200" /></a></li>');
    }
    $('body').keyup(function (e) {
        let key = e.keyCode;
        let img = $('.ui-dialog-content img');
        if(img.length === 1) {
            if(key >= 37 && key <= 40 ) {
                switch(key) {
                    case 37:  // 左
                    // case 38: // 上
                        player('', '-');
                        break;
                    case 39:  // 右
                    // case 40:  // 下
                        player('', '+');
                        break;
                }
            } else if(key === 13) {
                //d.close().remove();
                if(!playerTimer) {
                    playerTimer = setInterval(()=>{
                        player('▶ ', '+');
                        console.info('player ' + playerTimer + ', imgIndex ' + imgIndex);
                    }, 10000);
                    player('▶ ', '+');
                } else {
                    clearInterval(playerTimer);
                    $('.ui-dialog-content .dialog_title').text($('.ui-dialog-content .dialog_title').text().replace('▶', ''));
                    playerTimer=0;
                }
            }
        }
    });
});
</script>
</body>
</html>
'''


def check_excludes(filename, excludes=None):
    if excludes is not None and excludes != '':
        for ex_k in excludes:
            app.logger.info(ex_k + '    ' + filename)
            if ex_k in filename:
                return False
    return True


def get_img_list(path, name, allow_ext='.jpg', excludes=None):
    '''
    获取图片文件列表
    '''
    root_files = os.listdir(path)
    img_list = []
    root_files = sorted(root_files, key=lambda x: pinyin.get(x, format="strip"), reverse=False)
    for f in root_files:
        if os.path.isdir(os.path.join(path, f)):
            continue
        f_ext = os.path.splitext(f)[1]
        if f_ext == '' or not f_ext in allow_ext:
            continue
        if name is not None and name.strip() != '':
            name = name.strip()
            if not name.lower() in f.lower():
                continue
        if not check_excludes(filename=f, excludes=excludes):
            continue
        f = urllib.parse.quote(f)
        img_list.append(f)
    return img_list


@app.route('/', methods=['get', 'post'])
def home_root():
    '''
    默认首页，不提供服务，返回404
    '''
    return '', 404


@app.route('/home', methods=['get', 'post'])
def home():
    '''
    工作页，图片列表展示
    '''
    global ROOT_DEF
    req_name: str = req.args.get('name')
    req_path: str = req.args.get('path')
    if req_path is not None and req_path != '' and os.path.isdir(req_path):
        ROOT_DEF = req_path
    else:
        ROOT_DEF = ROOT
    excludes = None
    safe_checked = ''
    if req.args.get('safe') == '1':
        excludes = '乳穴'
        safe_checked = 'checked'
    img_list: list = get_img_list(path=ROOT_DEF, name=req_name, allow_ext='.jpg', excludes=excludes)
    html_list = []
    for idx, f in enumerate(img_list):
        img_list[idx] = urllib.parse.unquote(f)
    html = HOME_TEMPLATE.replace('{search_value}', ('' if req_name is None else req_name))
    html = html.replace('{search_path}', ('' if req_path is None or req_path.strip() == '' else req_path.strip()))
    html = html.replace('{imgs_list}', '\n'.join(html_list))
    html = html.replace('{imgs_arr}', json.dumps(img_list, ensure_ascii=False, separators=(', \n    ', ':')))
    html = html.replace('{safe_checked}', safe_checked)
    # print(json.dumps(img_list, ensure_ascii=False))
    return html


@app.route('/viewer', methods=['get', 'post'])
def viewer():
    '''
    图片缩略预览与图片查看
    '''
    name: str = req.args.get('name')
    resize: str = req.args.get('resize')
    if resize != '1':
        resize = '0'
    img_path = os.path.join(ROOT_DEF, name)
    cache_key = hashlib.md5((img_path + "_" + resize).encode()).hexdigest()
    img_bytes = cache.get(cache_key)
    # img_bytes = None
    print('img_bytes is None: %s' % str(img_bytes is None))
    if img_bytes is None:
        img = Image.open(img_path)
        img_bytes = io.BytesIO()
        if resize == '1':
            img = img.resize((200, 113), Image.ANTIALIAS)
        img.save(img_bytes, format='JPEG')
        img_bytes = img_bytes.getvalue()
        img_base64 = base64.b64encode(img_bytes).decode('utf-8')
        cache_result = cache.set(cache_key, img_base64, timeout=0)
        # print(str(cache_result))
        # print(len(img_base64))
        app.logger.info('cache-create %s %s' % (cache_key, img_path))
    else:
        img_bytes = base64.b64decode(img_bytes)
        # img_bytes = img_bytes.encode()
        app.logger.info('cache-pass %s %s' % (cache_key, img_path))
    return send_file(io.BytesIO(img_bytes), mimetype='image/jpeg')


@app.route('/favicon.ico')
def favicon():
    icon_data = 'AAABAAEAFBQAAAEAIAACBQAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAUAAAAFAgDAAAAulftPwAAAAFzUkdCAdnJLH8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAJ/UExURQAAAAAAAAAAAAAAAAAAAB8YGKuMg9Ktot25reLBtOnJu+vLvgAAAAAAAAIBAV1KRcmgldaxpd+8sOXFuOvKve/Owe/PwgAAAAAAAAEAAAwICJBsZtGkmduyqOK9subGuezKvu/OwfDQw/HRxAAAAAEAASwfHrmHfdimmeC0qOW/tOfHuu3Lv/DQw/HQxPPRxQAAAAIBAmFBPs2Qg92omOG1qeXAtOjHuvDPw/LRxPLSxRgODZhhV9iUhOGoluO0pebAs+fIuu/NwPDOwvHQw/LSxQEBAVUxLcB2aN2XhOOnleW0o+a+r+jFuezKve7MwPHPw/HQw4dLQc99bOGYhuSoluSyoOS7q+bCtujGuurJvOzLvu/NwTAaGMR2adJ8bOCWhOKmk+Gum+K4p+S/s+bDt+XGuWQ5M9yOgtB7bNqSf96ij96pl+C0o+K8ruPAtOPCteXEt+bFuefGujgbGaljWLVnWMyGc9SYhdagjtuund22qN+7r+C+suLBtOTCth8OC247MbFwYMKIdsyWhNOllNeuodi0qNm2qdu3q926rt+7sAIAASUREIdSR612abuGeceYis6lmNGtodOvotWxpNaypgMBATUgG4VZUKNzabSIfsGaj8ejl8mlmcuoms6qnM+sngAAAAMCAjokIHxWT5dzaqqGeraShryYjL+ajsKbkMSckQAAAAUDAy0fHGhOR4tsYp98cqmFeq6Lf7KOgrWQhAAAAAAAAAECACEZFl5GQH5hWo9vZZd3bJ99caaDdwAAAAAAABsUEkk0L2ZMQ3hbUIlrXwAAAAAAAAAAAQkGBicbGEw3MG5USAAAAAAAAAAAAAAAAAAAAAAAAfWyuIwAAADVdFJOUwAgp/r///////qXGEHj/////////+1XQsj////////////XNuT////////////JG6j///////////6H/////////////9z///////////////3//////////////////////////////////////////////////////////////////////////////////////////////////////////vv////////////dl//////////+dxjt////////zhdY1////+RGNv7//s5GARqH3P13F9p8ThMAAAE7SURBVHicY2BgYGBkYmYBAlY2dg5OLm4GMODhBQmx8PELCAoJi4iKgcTEJcBiklLSMrJy8gqKSspAvSpgMRZVNXUNTS1tHUVdPX0GA4gYi6GRsYmpmbmOooWlFQMzVNDaxtbO3sFRx8nZxZUBKubm7uHp5e3j6+fvHBAIEzQMCg4JDQuPiIyKjnGGCcbGxSckJiWnpKZpR/nBBNMzMrOyc3Lz8gsKi4phgiWlZeUVlVXVNUK1dfVwMxsam5pbWtvaOzq7umGCqj29ff0TJk6aPGXqtHaYIIvk9BkzZ82eM3fe/AULGRbBRRcvWbps+YqVq1avYVjLAhddt37Dxk2bt2zdxrB9B1yUZeeu3Xv27tt/gIHh4CGEWsPDR44eOw4K0BMSJ+HCp06fOXsOHPTnL1y8BBW8ePkKUAAAS+tfAHoHGgwAAAAASUVORK5CYII='
    icon_bytes = base64.b64decode(icon_data)
    return send_file(io.BytesIO(icon_bytes), mimetype='image/x-icon')


if __name__ == '__main__':
    ROOT = ''
    ROOT_DEF = ''
    args = argparse.ArgumentParser(description="")
    args.add_argument('--root', type=str, default='.', help='图片资源目录')
    x = args.parse_args()
    ROOT_DEF = ROOT = x.root
    app.run(host='0.0.0.0', port=APP_PORT, debug=True)
